﻿using System;
using System.Collections.Generic;

using System.Text;
using System.CodeDom.Compiler;
using Flame.Reflection;

namespace Flame.Dlr
{
    public interface ICompilable : IExecutable
    {
        void AddAssemblyDestination(IExecutable exec);
    }

    public abstract class CodeDomCompiler : ICompilable
    {
        public class CompilationResult
        {
            public ResultAssembly ResultAssembly { get; set; }
            public IExecutable Exec { get; set; }
        }

        List<IExecutable> _execs = new List<IExecutable>();
        List<AssemblyWrapper> _assembliesToLoad = new List<AssemblyWrapper>();



        public CodeDomCompiler(/*IEnumerable<IExecutable> execs*/)
        {
            /*var lexec = execs.ToList<IExecutable>();
            lexec.Add(this);
            _execs = lexec.ToArray();*/
        }

        public void AddAssemblyDestination(IExecutable exec)
        {
            _execs.Add(exec);
        }

        public abstract CodeDomProvider CodeDomProvider { get; }

        public Result Execute(string script)
        {
            using (CodeDomProvider foo = CodeDomProvider)
            {
                try
                {

                    var parameters = new System.CodeDom.Compiler.CompilerParameters();
                    parameters.GenerateInMemory = false;
                    parameters.GenerateExecutable = false;
                    parameters.IncludeDebugInformation = false;

                    foreach (AssemblyWrapper assembly in _assembliesToLoad)
                    {
                        try
                        {
                            parameters.ReferencedAssemblies.Add(assembly.Location);
                        }
                        catch
                        {
                        }
                    }

                    try
                    {
                        var res = foo.CompileAssemblyFromSource(parameters, script);
                        if (res.Errors.HasErrors)
                            return new Result() { Data = res.Errors[0] };


                        foo.Dispose();

                        List<CompilationResult> errors = new List<CompilationResult>();
                        ResultAssembly ra = null;
                        foreach (IExecutable c in _execs)
                        {
                            ra = c.AddAssembly(new AssemblyWrapper(res.CompiledAssembly));
                            if (ra != null && ra.Exception != null)
                                errors.Add(new CompilationResult() { ResultAssembly = ra, Exec = c });
                        }
                        if (errors.Count != 0) return new Result() { Data = errors };
                        return new Result() { Data = res.CompiledAssembly };
                    }
                    catch (Exception e)
                    {
                        return new Result() { Data = e };
                    }
                }
                catch (System.Exception e)
                {
                    return new Result() { Data = e };
                }
            }
        }

        public void AddVariable(Variable variable) { }
        public void ClearScope(ScopeParameters scopeParameters) { if (scopeParameters != null) scopeParameters.Apply(this); }

        public ResultAssembly AddAssembly(AssemblyWrapper ainfo)
        {
            _assembliesToLoad.Add(ainfo);
            return new ResultAssembly() { Loaded = false, Exception = null, Language = GetType() };
        }

        public IIntellisense Intellisense { get { return null; } }
    }
}
